package test

import (
	"crypto/rand"
	"fmt"
	"math/big"
)

// GetPrime 获取质数
func GetPrime() *big.Int {
	prime, err := rand.Prime(rand.Reader, 7)
	if err != nil {
		fmt.Println("get prime number err：", err.Error())
		return nil
	}
	return prime
}

// GCD 获取p和q的最大公约数
func GCD(p, q *big.Int) *big.Int {
	var t = big.NewInt(0)
	return t.GCD(big.NewInt(1), big.NewInt(1), p, q)
}

// RandExponent 求E：1<E<L且E与L互质
func RandExponent(l *big.Int) *big.Int {
	var t = big.NewInt(2)
	var gcd *big.Int
	for t.Cmp(l) <= 0 {
		gcd = GCD(t, l) // 获取E与L的最大公约数:如果最大公约数为1则E与L互质
		if gcd.Cmp(big.NewInt(1)) == 0 {
			return t
		}
		t = big.NewInt(0).Add(t, big.NewInt(1))
	}
	return nil
}

// ModOne 求模余1：即求D，1<D<L且E*D mod L == 1
func ModOne(l, e *big.Int) *big.Int {
	var t = big.NewInt(2)
	var result, mul *big.Int
	for t.Cmp(l) < 0 {
		mul = big.NewInt(0).Mul(e, t)
		result = big.NewInt(0).Mod(mul, l)
		if result.Cmp(big.NewInt(1)) == 0 {
			return t
		}
		t = big.NewInt(0).Add(t, big.NewInt(1))
	}
	return nil
}

// TestRsa RSA加解密演示
func TestRsa() {

	// 1，输入待加密明文
	var mm string
	fmt.Print("输入待加密明文:")
	_, _ = fmt.Scanf("%s", &mm)

	// 2，生成RSA解密的密钥对
	// 2.1 生成两个不相等的质数
	p := GetPrime()
	q := GetPrime()
	for p.Int64() == q.Int64() {
		q = GetPrime()
	}

	// 2.2生成N=p*q
	var n = big.NewInt(0).Mul(p, q)

	// 2.3 生成中间数L：L为(p-1)和(q-1)的最小公倍数=(p-1)*(q-1)/两数的最大公约数
	var gcd = GCD(big.NewInt(0).Add(p, big.NewInt(-1)), big.NewInt(0).Add(q, big.NewInt(-1)))
	var f = big.NewInt(0).Mul(big.NewInt(0).Add(p, big.NewInt(-1)), big.NewInt(0).Add(q, big.NewInt(-1)))
	var l = big.NewInt(0).Div(f, gcd)

	// 2.4生成E：1<E<L且E,L互质
	var e = RandExponent(l)

	// 2.5生成D：1<D<L且ED mod l == 1
	var d = ModOne(l, e)

	fmt.Printf("质数p=%d,q=%d;n=p*q=%d;中间数l=%d;e=%d;d=%d\n", p, q, n, l, e, d)

	// 3,加密
	// 3.1明文转换为二进制形式
	content, _ := big.NewInt(0).SetString(mm, 0)
	var z big.Int
	z.Exp(content, e, n)
	fmt.Println("加密后的密文为:", z.String())

	// 4,解密
	// 4.1密文转换为二进制
	sContent := big.NewInt(0).SetInt64(z.Int64())
	var s big.Int
	s.Exp(sContent, d, n)
	fmt.Println("解密后的明文为:", s.String())
}
